package com.itheima.pinda.zuul.filter;

import cn.hutool.core.util.StrUtil;
import com.itheima.pinda.authority.dto.auth.ResourceQueryDTO;
import com.itheima.pinda.authority.entity.auth.Resource;
import com.itheima.pinda.common.constant.CacheKey;
import com.itheima.pinda.context.BaseContextConstants;
import com.itheima.pinda.exception.code.ExceptionCode;
import com.itheima.pinda.zuul.api.ResourceApi;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import net.oschina.j2cache.CacheChannel;
import net.oschina.j2cache.CacheObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description: 鉴权处理过滤器
 * @Author: ysf
 * @CreateTime: 2024-11-14  15:36
 * @Version: 1.0
 */
@Component
@Slf4j
public class AccessFilter extends BaseFilter {

    @Autowired
    CacheChannel cacheChannel;
    @Qualifier("com.itheima.pinda.zuul.api.ResourceApi")
    @Autowired
    ResourceApi resourceApi;


    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER + 10;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 鉴权处理逻辑
     *
     * @param
     * @return java.lang.Object
     */
    @Override
    public Object run() throws ZuulException {
        //第1步：判断当前请求uri是否需要忽略
        if (getIgnoreToken()) {
            //需要忽略 直接放行
            return null;
        }
        //第2步：获取当前请求的请求方式和uri，拼接成GET/user/page这种形式，称为权限标识符
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();

        String method = request.getMethod();
        String uri = request.getRequestURI();
        uri = StrUtil.subSuf(uri, zuulPrefix.length());
        uri = StrUtil.subSuf(uri, uri.indexOf("/", 1));
        //权限标识符
        String permission = method + uri;


        log.info("权限标识符:{}",permission);

        //第3步：从缓存中获取所有需要进行鉴权的资源(同样是由资源表的method字段值+uri字段值拼接成)
        CacheObject resourceNeedAuth = cacheChannel.get(CacheKey.RESOURCE, CacheKey.RESOURCE_NEED_TO_CHECK);
        //如果没有获取到则通过Feign调用权限服务获取并放入缓存中
        List<String> resourceData = null;
        if (resourceNeedAuth.getValue() == null) {
            resourceData = resourceApi.list().getData();

            if (resourceData != null && resourceData.size() > 0) {
                cacheChannel.set(CacheKey.RESOURCE, CacheKey.RESOURCE_NEED_TO_CHECK, resourceData);
            }
        }
        //第4步：判断这些资源是否包含当前请求的权限标识符
        if (resourceData != null) {
            long count = resourceData.stream().filter(permission::startsWith).count();
            //如果不包含当前请求的权限标识符，则返回未经授权错误提示
            if (count == 0) {
                errorResponse(ExceptionCode.UNAUTHORIZED.getMsg(), ExceptionCode.UNAUTHORIZED.getCode(), 200);
                return null;
            }
        }

        //第5步：如果包含当前的权限标识符，则从zuul header中取出用户id，根据用户id取出缓存中的用户拥有的权限，
        String userId = currentContext.getZuulRequestHeaders().get(BaseContextConstants.JWT_KEY_USER_ID);
        CacheObject cacheObject = cacheChannel.get(CacheKey.USER_RESOURCE, userId);
        List<String> userResource = (List<String>) cacheObject.getValue();

        //如果没有取到则通过Feign调用权限服务获取并放入缓存
        if (userResource == null) {
            //构建新对象
            ResourceQueryDTO resourceQueryDTO = ResourceQueryDTO.builder().userId(Long.valueOf(userId)).build();
            List<Resource> result = resourceApi.visible(resourceQueryDTO).getData();
            if (result != null && result.size() > 0) {
                //筛选出method字段值+url字段值
                userResource = result.stream().map((Resource r) -> {
                    return r.getMethod() + r.getUrl();
                }).collect(Collectors.toList());
            }

            //将用户id作为key，将method字段值+url字段值作为（权限）存入缓存
            cacheChannel.set(CacheKey.USER_RESOURCE, userId, userResource);

        }
        //判断当前请求的权限标识符是否是用户拥有的权限
        long count = userResource.stream().filter(permission::startsWith).count();

        //第6步：如果用户拥有的权限包含当前请求的权限标识符则说明当前用户拥有权限，直接放行
        if (count > 0) {
            return null;
        } else {
            //第7步：如果用户拥有的权限不包含当前请求的权限标识符则说明当前用户没有权限，返回未经授权错误提示
            log.warn("用户{}没有访问{}资源的权限", userId, permission);
            errorResponse(ExceptionCode.UNAUTHORIZED.getMsg(), ExceptionCode.UNAUTHORIZED.getCode(), 200);
            return null;
        }

    }
}
